﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EzDesk.Attendance.AttendanceImpl.Repositories;
using EzDesk.Attendance.AttendanceImpl;
using EzDesk.Attendance.Model;
using EzDesk.Attendance.AttendanceImpl.Filters;

namespace EzDesk.Attendance.AttendanceImpl
{
    public class AttendanceEvaluatorFactory
    {
        AttendanceService _attendanceService;
        AttendanceRepository _repository;

        public AttendanceEvaluatorFactory(AttendanceService attendanceService, AttendanceRepository repository)
        {
            this._attendanceService = attendanceService;
            this._repository = repository;
        }
        
        public IAttendanceEvaluator CreateEvaluator(Guid userId, DateTime day)
        {

            // 获得工作日期信息
            var dayInfo = _attendanceService.GetDayInfo(day);

            // 获得考勤人当日考勤信息
            var attendanceInfos = _repository.GetAttendanceInfos()
                .ByUserId(userId)
                .InDay(day)
                .IsExtrawork(false);

            var onDutyInfo = attendanceInfos.ByActionType(DutyTypeEnum.OnDuty).SingleOrDefault();
            var offDutyInfo = attendanceInfos.ByActionType(DutyTypeEnum.OffDuty).SingleOrDefault();

            // 获得考勤人当日假期信息
            var leaveInfo = _attendanceService.GetDayLeaveInfo(userId, day);

            IAttendanceEvaluator evaluator = null;

            AttendanceEvaluationInfo evalBaseInfo = new AttendanceEvaluationInfo()
            {
                DayInfo = dayInfo,
                LeaveInfo = leaveInfo,
                OnDutyAction = onDutyInfo,
                OffDutyAction = offDutyInfo,
            };

            var customizedShiftWork = _repository.GetCustomizedShiftWorks()
                .ByUserIdAndDay(userId, day)
                .SingleOrDefault();

            if (customizedShiftWork != null)
            {
                evalBaseInfo.CustomizedShiftWork = customizedShiftWork;
            }

            if (day.Date < DateTime.Today)
            {
                evaluator = new AttendanceInThePastEvaluator(evalBaseInfo)
                    {
                        AttendanceService = _attendanceService,
                        Repository = _repository,
                    };
            }
            else if (day.Date == DateTime.Today)
            {
                evaluator = new AttendanceTodayEvaluator(evalBaseInfo)
                {
                    AttendanceService = _attendanceService,
                    Repository = _repository,
                };
            }

            return evaluator;
        }
    }

    public class AttendanceTodayEvaluator : AttendaceEvaluatorBase, IAttendanceEvaluator
    {
        public AttendanceTodayEvaluator(AttendanceEvaluationInfo evalInfo)
        {
            _evalInfo = evalInfo;
        }

        public override AttendanceEvaluationInfo Evaluate()
        {
            var info = _evalInfo;

            if (info.DayInfo.NeedToWork)
            {
                //标准上下班时间
                DateTime stdDayStartWorkTime = _attendanceService.GetAttendanceConfig().StdOnDutyTime.ToDateTime(DayInfo.Date);
                DateTime stdDayEndWorkTime = _attendanceService.GetAttendanceConfig().StdOffDutyTime.ToDateTime(DayInfo.Date);

                if (OnDutyInfo != null)
                {
                    EvaluateLateOnDuty(ref info, stdDayStartWorkTime);
                }
                if (OffDutyInfo != null)
                {
                    EvaluateEarlyLeaveOffDuty(ref info, stdDayEndWorkTime);
                }
            }
            info.IsLeaveWholeDay = LeaveInfo.IsWholeDay;

            info.IsExtraWork = false;
            info.IsCompleteAttendance = false;
            info.IsFragmentaryAttendance = false;
            info.IsAbsence = false;
            info.IsLeaveForWork = false;

            return info;
        }
    }

    public class AttendanceInThePastEvaluator : AttendaceEvaluatorBase, IAttendanceEvaluator
    {
        public AttendanceInThePastEvaluator(AttendanceEvaluationInfo evalInfo)
        {
            _evalInfo = evalInfo;
        }

        public override AttendanceEvaluationInfo Evaluate()
        {
            var info = _evalInfo;


            //标准上下班时间
            DateTime stdDayStartWorkTime = _attendanceService.GetAttendanceConfig().StdOnDutyTime.ToDateTime(DayInfo.Date);
            DateTime stdDayEndWorkTime = _attendanceService.GetAttendanceConfig().StdOffDutyTime.ToDateTime(DayInfo.Date);

            //这天经过假期修正的正常工作时间
            DateTime theDayStartWorkTime = stdDayStartWorkTime;
            DateTime theDayEndWorkTime = stdDayEndWorkTime;

            //当日是否有假期
            if (LeaveInfo != null && LeaveInfo.Leaves.Count > 0)
            {
                // 全天假期
                if (LeaveInfo.IsWholeDay)
                {
                    info.IsLeaveWholeDay = true;
                    if (LeaveInfo.FirstLeave.IsForWork)
                    {
                        info.IsLeaveForWork = true;
                    }
                }
                else
                {
                    // 有假期，计算是否需要上下班考勤，修正标准上下班时间
                    if (LeaveInfo.FirstLeave.StartTime <= stdDayStartWorkTime)
                    {
                        // 上班时间开始有假期，则延后上班打卡时间
                        theDayStartWorkTime = LeaveInfo.Leaves.First().EndTime;
                    }
                    if (LeaveInfo.LastLeave.EndTime >= stdDayEndWorkTime)
                    {
                        // 下班时间有假期，则提前下班打卡时间
                        theDayEndWorkTime = LeaveInfo.Leaves.Last().StartTime;
                    }
                }
            }

            //是否完整签到
            info.IsCompleteAttendance = (OnDutyInfo != null && OffDutyInfo != null);
            info.IsFragmentaryAttendance = (OnDutyInfo == null && OffDutyInfo != null) || (OnDutyInfo != null && OffDutyInfo == null);

            //它这天是否需要工作
            bool heNeedToWork = DayInfo.NeedToWork;
            if (info.IsLeaveWholeDay && !info.IsLeaveForWork)
            {
                heNeedToWork = false;
            }

            //当日是否需要工作
            if (heNeedToWork)
            {
                if (info.IsLeaveForWork)
                {
                    info.IsLate = false;
                    info.IsEarlyLeave = false;
                    info.IsAbsence = false;
                }
                else
                {
                    //需要工作，且没有整日非工作假期
                    if (info.IsCompleteAttendance)
                    {
                        info.IsAbsence = false;
                        //判断迟到
                        EvaluateLateOnDuty(ref info, theDayStartWorkTime);
                        //判断早退
                        EvaluateEarlyLeaveOffDuty(ref info, theDayEndWorkTime);

                    }
                    else
                    {
                        // 当天旷工
                        info.IsAbsence = true;
                    }
                }
            }
            else
            {
                //不需要工作
                // 没有迟到，早退
                info.IsLate = false;
                info.IsEarlyLeave = false;
                info.IsAbsence = false;

                if (info.IsCompleteAttendance || info.IsLeaveForWork)
                {
                    EvaluateExtraWork(ref info);
                }
            }
            return info;
            
        }
    }


    public abstract class AttendaceEvaluatorBase : IAttendanceEvaluator
    {
        protected AttendanceEvaluationInfo _evalInfo;

        protected DayInfo DayInfo { get { return _evalInfo.DayInfo; } }
        protected DayLeaveInfo LeaveInfo { get { return _evalInfo.LeaveInfo; } }

        protected AttendanceInfo OnDutyInfo { get { return _evalInfo.OnDutyAction; } }
        protected AttendanceInfo OffDutyInfo { get { return _evalInfo.OffDutyAction; }  }
        
        protected AttendanceRepository _repository;
        protected AttendanceService _attendanceService;

        public AttendanceRepository Repository
        {
            set
            {
                _repository = value;
            }
        }

        public AttendanceService AttendanceService
        {
            set
            {
                _attendanceService = value;
            }
        }

        /// <summary>
        /// 评估加班
        /// </summary>
        /// <param name="info"></param>
        protected void EvaluateExtraWork(ref AttendanceEvaluationInfo info)
        {
            info.IsExtraWork = true;
            if (info.IsLeaveForWork)
            {
                info.ExtraWorkTime = 480;
            }
            else
            {
                info.ExtraWorkTime = Math.Max((int)(OffDutyInfo.ActionTime - OnDutyInfo.ActionTime).TotalMinutes, 480);
            }
        }

        /// <summary>
        /// 评价早退
        /// </summary>
        /// <param name="info"></param>
        /// <param name="theDayStartWorkTime"></param>
        /// <param name="OnDutyInfo"></param>
        protected void EvaluateEarlyLeaveOffDuty(ref AttendanceEvaluationInfo info, DateTime theDayEndWorkTime)
        {
            bool isEarlyLeave = OffDutyInfo.ActionTime < theDayEndWorkTime;
            if (isEarlyLeave)
            {
                info.IsEarlyLeave = true;
                info.EarlyMiniutes = (int)((theDayEndWorkTime - OffDutyInfo.ActionTime).TotalMinutes);
            }
            else
            {
                info.IsEarlyLeave = false;
                info.EarlyMiniutes = 0;
            }
        }

        /// <summary>
        /// 评价迟到
        /// </summary>
        /// <param name="info"></param>
        /// <param name="theDayStartWorkTime"></param>
        /// <param name="onDutyInfo"></param>
        protected void EvaluateLateOnDuty(ref AttendanceEvaluationInfo info, DateTime theDayStartWorkTime)
        {
            //get the dutyInfo of last day
            DateTime today = theDayStartWorkTime.Date;

            DateTime lastDay = today.AddDays(-1);
            var lastDayAttendanceInfos = _repository.GetAttendanceInfos()
                .ByUserId(OnDutyInfo.UserId)
                .InDay(lastDay);

            var lastDayOnDuty = lastDayAttendanceInfos.ByActionType(DutyTypeEnum.OnDuty).FirstOrDefault();
            var lastDayOffDuty = lastDayAttendanceInfos.ByActionType(DutyTypeEnum.OffDuty).FirstOrDefault();

            int lastDayWorkTime = CalculateWorktime(lastDayOnDuty, lastDayOffDuty);

            // TODO: 5 mins???
            DateTime LateOnDutyTime = theDayStartWorkTime.AddMinutes(5);

            if (lastDayWorkTime > 12.5 * 60 && lastDayOffDuty.ActionTime.Hour >= 22)
            {
                theDayStartWorkTime = theDayStartWorkTime.AddHours(1);
                LateOnDutyTime = theDayStartWorkTime;
            }

            bool isLate = OnDutyInfo.ActionTime > LateOnDutyTime;
            if (isLate)
            {
                info.IsLate = true;
                info.LateMiniutes = (int)((OnDutyInfo.ActionTime - theDayStartWorkTime).TotalMinutes);
            }
            else
            {
                info.IsLate = false;
                info.LateMiniutes = 0;
            }
        }

        protected int CalculateWorktime(AttendanceInfo onDuty, AttendanceInfo offDuty)
        {
            int lastDayWorkTime = 0;
            if (onDuty != null && offDuty != null)
            {
                lastDayWorkTime = (int)(offDuty.ActionTime - onDuty.ActionTime).TotalMinutes;
            }
            return lastDayWorkTime;
        }

        #region IAttendanceEvaluator Members

        public abstract AttendanceEvaluationInfo Evaluate();

        #endregion
    }

    public interface IAttendanceEvaluator
    {
        AttendanceEvaluationInfo Evaluate();
    }


}